💡 论文解读与参考来源:本文内容基于对微软论文 From Local to Global: A Graph RAG Approach to Query-Focused Summarization (arXiv:2404.16130) 的深入研读、工程实践与架构解读。
在这篇论文里,“社区”不是社交网络里的“用户圈子”,而是一个图结构中的“高内聚子图”——也就是:
简单说:社区 = “一个图中的主题簇/模块/子图”。
论文在 GraphRAG 的流程里,先把文档抽取成知识图谱:
然后,GraphRAG 进一步对这个知识图做“社区检测”(community detection)。
也就是说:
基于图的连接结构,把相互靠近、语义上下文相似的实体分组成一个个社区。
这一步对应论文中的:
一个社区可以理解为一组“紧密相连”的节点集合,具有这样的特点:
例如在新闻、企业、科研、政府等语料库中,一个社区可能对应:
社区并不只是网络拓扑上的“簇”,它还承载语义信息。因为节点和边都来自文档抽取,所以社区也同时代表:
因此,社区可以被看成:
一个“知识模块”或“主题单元”,它是全局语料库的一部分。
GraphRAG 的目标不是只检索单个事实,而是回答“全局意义构建”问题。比如:
如果直接面对整个大图,模型很难一次性理解所有信息。于是 GraphRAG 把大图拆成多个社区,再在社区内部做摘要,最后在社区之间做合并与整合。
这使得系统具备以下优点:
论文中的流程是:
论文强调:社区检测不是只做一层,而是递归地做。也就是:
这种结构很关键,因为它提供了不同粒度的知识视角:
因此,社区不是一维的,而是一个“层次化主题地图”。
MERMAID
社区的价值不只是“分组”,而是在于每个社区都可以生成一段摘要。论文中的“社区摘要”是关键中间产物。
对于最底层社区,摘要通常聚合:
高层社区的摘要则可以基于下层社区摘要继续生成:
这意味着:
社区以后不是“只为了切图”,而是为检索和生成准备的主题摘要单元。
论文中“社区摘要 → 社区答案 → 全球答案”是核心流程:
MERMAID
若用户查询某个主题,系统会取出相关社区摘要,并把它们随机打乱、切块处理。
这样做的目的是:
对每个社区摘要,LLM 会回答:
同时,LLM 要给出一个分数(0~100),表示该答案对回答目标问题的帮助程度。
最终只保留最有帮助的社区答案,合并成一个系统最终输出。
这说明:
社区在查询阶段不是被当成一个静态标签,而是成为“候选证据单元”,最终被筛选和整合成答案。
[!note] 关联视角 [[graphRAG-community-concept]] 是对 [[2404.16130v2_zh]] 中“知识图谱 → 图社区 → 社区摘要 → 社区答案 → 全球答案”流程的概念化解释。
- [[2404.16130v2_zh]]:原论文主文档
- [[graphRAG-community-concept]]:社区定义与语义解释
Graph community:图社区检测的核心对象Community summary:社区摘要Community answer:社区中间答案Global answer:最终全局答案
ts// 关联关系: // 文档 -> 知识图谱 -> 社区检测 -> 社区摘要 -> 社区答案 -> 全局答案 type CommunitySummary = { communityId: string; level: number; content: string; }; type CommunityView = { communityId: string; answer: string; score: number; // 0~100 }; async function generateGlobalAnswer( query: string, communities: CommunitySummary[] ): Promise<string> { // 1) 社区切片:把社区摘要按 token 切块 const chunks = sliceCommunities(communities, 1200); // 2) 对每个社区切片生成中间答案,并打分 const candidates: CommunityView[] = []; for (const chunk of chunks) { const view = await generateCommunityView(query, chunk); if (view && view.score > 0) { candidates.push(view); } } // 3) 排序过滤:按分数降序,保留高价值观点 const ranked = candidates .sort((a, b) => b.score - a.score) .filter(v => v.score >= 1); // 4) 迭代拼接到最终上下文,直到不超 token 上限 let finalContext = ""; for (const item of ranked) { if (estimateTokens(finalContext + item.answer) > 8192) break; finalContext += item.answer + "\n\n"; } // 5) 生成最终全局答案 return await llmAnswer(query, finalContext); } function sliceCommunities( communities: CommunitySummary[], chunkSize: number ): CommunitySummary[][] { const shuffled = shuffle(communities); const result: CommunitySummary[][] = []; let bucket: CommunitySummary[] = []; let tokens = 0; for (const c of shuffled) { const cTokens = estimateTokens(c.content); if (bucket.length && tokens + cTokens > chunkSize) { result.push(bucket); bucket = []; tokens = 0; } bucket.push(c); tokens += cTokens; } if (bucket.length) result.push(bucket); return result; } async function generateCommunityView( query: string, chunk: CommunitySummary[] ): Promise<CommunityView | null> { const text = chunk.map(c => c.content).join("\n\n"); const prompt = ` 用户问题: ${query} 社区摘要: ${text} 你需要: 1) 给出一个对问题有帮助的中间答案 2) 输出 0~100 的帮助分数 `; const raw = await llmCall(prompt); const parsed = JSON.parse(raw); const answer = String(parsed.answer ?? "").trim(); const score = Number(parsed.score ?? 0); if (!answer || score <= 0) return null; return { communityId: chunk.map(c => c.communityId).join(","), answer, score, }; } async function llmAnswer(query: string, context: string): Promise<string> { return await llmCall(` 问题: ${query} 参考信息: ${context} `); }
MERMAID
这段伪代码本质上就是把论文中的这条链路写成了工程化表达:
它与论文中的核心思想一一对应:
community detection = 社区检测community summary = 社区摘要community answer = 社区答案global answer = 全局答案可以把 GraphRAG 的社区理解成:
如果把整个知识库看成一张巨大的城市地图,那么社区就是:
每个区有自己的主题、节点、关系和摘要,而最终的答案就是从这些区中筛选最有价值的信息重组出来。
在 GraphRAG 中,“社区”指的是:
通过社区检测算法把知识图拆分成若干个紧密连接、语义相近、可独立总结的主题模块;这些社区被抽象成社区摘要,随后在查询阶段用于生成中间答案并最终聚合出全局答案。
它是 GraphRAG 从“检索单条事实”走向“理解整个知识库结构”的关键抽象层。